package velbus {
    import flash.events.*;
    import flash.net.Socket;
    import flash.system.Security;
    import flash.utils.ByteArray;
    import flash.utils.setTimeout;
	import flash.utils.Timer;
	import mx.utils.StringUtil;
	import velbus.modules.PushButtonAndTimer;
	import velbus.Packet;
    
    import mx.controls.TextArea;
    import mx.core.UIComponent;

    public class Velbus extends UIComponent{
       
        private var serverURL:String;
        private var portNumber:int;
        private var socket:Socket;
        private var ta:TextArea;
        private var state:int = 0;
        private var byteList:Array;
        static private var _sendQueue:/*Packet*/Array;
		static private var _sendQueueTimer:Timer;
		
        public function Velbus(server:String, port:int, output:TextArea) {
            // set class variables to the values passed to the constructor.
            serverURL = server;
            portNumber = port;
            ta = output;
			byteList = new Array();
            _sendQueue = new Array();
			_sendQueueTimer = new Timer(60);
			_sendQueueTimer.addEventListener(TimerEvent.TIMER, onSendQueueTriggerd);
            // Create a new Socket object and assign event listeners.
            socket = new Socket();
            socket.addEventListener(Event.CONNECT, connectHandler);
            socket.addEventListener(Event.CLOSE, closeHandler);
            socket.addEventListener(ErrorEvent.ERROR, errorHandler);
            socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            socket.addEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
            
            // Load policy file from remote server.
            //Security.loadPolicyFile("http://" + serverURL + "/crossdomain.xml");
            // Attempt to connect to remote socket server.
            try {
                msg("Trying to connect to " + serverURL + ":" + portNumber + "\n");
                socket.connect(serverURL, portNumber);
            } catch (error:Error) {
                /*
                    Unable to connect to remote server, display error 
                    message and close connection.
                */
                msg(error.message + "\n");
                socket.close();
            }
        }
				
		private function onSendQueueTriggerd(e:TimerEvent):void 
		{
			//trace( "onSendQueueTriggerd : queue length: " + _sendQueue.length );
			var p:Packet = _sendQueue.shift();
			writeBytesToSocket(p.getRawPacket());
			if (_sendQueue.length == 0) {
				_sendQueueTimer.stop();
			}
		}
        
        /**
         * This method is called if the socket encounters an ioError event.
         */
        public function ioErrorHandler(event:IOErrorEvent):void {
            msg("Unable to connect to socket server. Velbuslink should run in server mode ...\n");
        }
        
        /**
         * This method is called by our application and is used to send data
         * to the server.
         */
        public function writeBytesToSocket(ba:ByteArray):void {
            socket.writeBytes(ba);
            socket.flush();
        }
        
		static public function addToSendQueue(p:Packet): void 
		{
			_sendQueue.push(p);
			//trace( "addToSendQueue : " + _sendQueue.length + " " + p);
			if (!_sendQueueTimer.running) {
				_sendQueueTimer.start();
			}
		}
        private function connectHandler(event:Event):void {
            if (socket.connected) {
                msg("Connected...\n");
				dispatchEvent(new VelbusEvent(VelbusEvent.CONNECTED));
            } else {
                msg("Unable to connect\n");
            }
        }
        
        /**
         * This method is called when the socket connection is closed by 
         * the server.
         */
        private function closeHandler(event:Event):void {
            msg("Closed...\n");
        }
        
        /**
         * This method is called if the socket throws an error.
         */
        private function errorHandler(event:ErrorEvent):void {
            msg(event.text + "\n");
        }
        
        /**
         * This method is called when the socket receives data from the server.
         */
        private function dataHandler(event:ProgressEvent):void {
			
            var n:int = socket.bytesAvailable;
			var ba: ByteArray = new ByteArray();
			
			socket.readBytes(ba);
			
			var pos:int = 0;
			
			do {
				//get size of next packet
				var size:int = ((ba[pos + 3] & 0x0F) % 256) + 6;
			
				var thisPacketBytes:ByteArray = new ByteArray();
				for (var i:int = 0; i <size ; i++) 
				{
					thisPacketBytes[i] = ba[pos + i];
				}
				
				var packet:Packet = new Packet(thisPacketBytes[2]);
				for (i = 0; i < thisPacketBytes.length; i++) 
				{
					packet.setRawByte(i, thisPacketBytes[i]);
				}
				
				dispatchEvent(new VelbusEvent(VelbusEvent.PACKET_RECEIVED, packet));
				ta.text += packet + "\r";
				ta.dispatchEvent(new Event(Event.CHANGE));
				setTimeout(setScroll, 100);
				pos += size;
			} while (pos < ba.length); //check id there are still other packets in the buffer
        }
        
        public function close():void {
        	socket.close();
        }
        
        public static function dec2hex( d:int ) : String {
		    var s:String = d.toString(16).toUpperCase();
			if (s.length == 1) s = "0" + s
			return s
		}
		

        
        /**
         * Append message to the TextArea component on the display list.
         * After appending text, call the setScroll() method which controls
         * the scrolling of the TextArea.
         */
        private function msg(value:String):void {
			
			ta.text += value;	
			
	        ta.dispatchEvent(new Event(Event.CHANGE));
            setTimeout(setScroll, 100);
        }

        /**
         * Scroll the TextArea component to its maximum vertical scroll 
         * position so that the TextArea always shows the last line returned
         * from the server.
         */
        public function setScroll():void {
            ta.verticalScrollPosition = ta.maxVerticalScrollPosition;
        }
    }
}
